package com.sm.healthy.device;

import static android.content.ContentValues.TAG;

import android.util.Log;

import com.sm.healthy.utils.DataUtils;
import com.sm.healthy.utils.StringUtils;


/**
 * Created by shenmai8 on 2018/4/19.
 */

public class Command {

    private static byte [] gPackLength = {
            0, // 00: 保留
            2, // 01: 系统复位信息
            5, // 02: 系统状态
            9, // 03: 系统自检
            5, // 04: 命令应答
            8, // 05: ECG波形数据
            5, // 06: 导联连接和信号信息
            5, // 07: 心率
            5, // 08: 保留 单通道心电波形数据，新增加
            4, // 09: 保留 pvc数量
            6, // 0a: 保留 arr心率失常
            7, // 0b: ST偏移 : 每秒发送一次
            9, // 0c: ST段波形值
            0, // 0d: 保留 心电节律
            9, // 0e: 保留 心电脉搏各两个字节包
            0, // 0f: 保留
            4, // 10: 呼吸波
            5, // 11: 呼吸率
            6, // 12: 窒息报警
            4, // 13: CVA报警信息
            0, // 14: 保留
            8, // 15: 体温数据
            5, // 16: SPO2波形数据
            7, // 17: SPO2数据
            7, // 18: IBP波形数据
            4, // 19: IBP状态
            10, // 1a: IBP压力
            6, // 1b: IBP校零和校准信息
            7, // 1c: IBP校零和校准时间
            0, // 1d: 保留
            0, // 1e: 保留
            0, // 1f: 保留
            7, // 20: NBP实时测量数据
            4, // 21: NBP测量结束包
            9, // 22: NBP测量结果1
            5, // 23: NBP测量结果2
            8, // 24: NBP测量状态
            0, // 25: 保留
            0, // 26: 保留
            0, // 27: 保留
            0, // 28: 保留
            0, // 29: 保留
            0, // 2a: 保留
            0, // 2b: 保留
            0, // 2c: 保留
            0, // 2d: 保留
            0, // 2e: 保留
            0, // 2f: 保留
            10, // 30: 保留 -> 返回设备ID
            4, // 31: 保留 -> 返回License写入状态
            10, // 32: 保留 -> 返回License、设备ID
            0, // 33: 保留
            0, // 34: 保留
            0, // 35: 保留
            0, // 36: 保留
            0, // 37: 保留
            0, // 38: 保留
            0, // 39: 保留
            0, // 3a: 保留
            0, // 3b: 保留
            0, // 3c: 保留
            0, // 3d: 保留
            0, // 3e: 保留
            0, // 3f: 保留
            2, // 40: 读取自检结果
            4, // 41: 接收数据选择
            4, // 42: 病人信息设置
            0, // 43: 保留
            0, // 44: 保留
            4, // 45: 导联模式选择:3,5导
            4, // 46: 导联方式选择
            4, // 47: 滤波方式选择
            4, // 48: 心电增益
            4, // 49: 1mV校准信号设置
            4, // 4a: 工频抑制设置
            4, // 4b: 起搏分析开关设置
            7, // 4c: ST测量的ISO和ST点
            4, // 4d: 设置心电分析通道
            2, // 4e: 心电自学习触发
            0, // 4f: 保留
            4, // 50: 呼吸增益
            0, // 51: 保留
            4, // 52: 窒息报警时间选择
            4, // 53: 体温探头类型设置
            4, // 54: SPO2设置
            2, // 55: NBP开始一次手动/自动测量
            2, // 56: NBP结束测量
            4, // 57: NBP测量周期设置
            2, // 58: NBP开始校准
            2, // 59: NBP复位
            2, // 5a: NBP漏气检测
            2, // 5b: NBP查询状态
            5, // 5c: NBP设置初次充气压力
            2, // 5d: NBP开始STAT测量
            2, // 5e: NBP查询结果
            5, // 5f: 保留 –> 静态压力校准 : pra=150~220 mmgh
            4, // 60: IBP设置压力名称
            4, // 61: IBP校零
            7, // 62: IBP1校准
            7, // 63: IBP2校准
            4, // 64: 设置数字滤波模式
            5, // 65: IBP查询
            0, // 66: 保留
            0, // 67: 保留
            0, // 68: 保留
            0, // 69: 保留
            0, // 6a: 保留
            0, // 6b: 保留
            0, // 6c: 保留
            0, // 6d: 保留
            0, // 6e: 保留
            0, // 6f: 保留
            2, // 70: 保留 -> 获取设备ID命令
            10, // 71: 保留 -> 写入License
            2, // 72: 保留 -> 获取License、设备ID命令
            0, // 73: 保留
            0, // 74: 保留
            0, // 75: 保留
            0, // 76: 保留
            0, // 77: 保留
            0, // 78: 保留
            0, // 79: 保留
            0, // 7a: 保留
            0, // 7b: 保留
            0, // 7c: 保留
            0, // 7d: 保留
            0, // 7e: 保留
            2, // 7f: 保留 -> 开始测量命令
    };

    public void setResolveCommandListener(ResolveCommandListener resolveCommandListener) {
        this.resolveCommandListener = resolveCommandListener;
    }

    private ResolveCommandListener resolveCommandListener;
    private int size=2048;
    private int readpos=0;
    private int writepos = 0;
    private int total = 0;
    private byte  byteBuffer[];

    public Command(){
        byteBuffer =new byte [2048];
    }

    public int remaining(){
        return size-total;
    };

    public void put(byte  []data,int count){
        int i;
        for(i=0;i<count;i++)
        {
            byteBuffer[writepos ++] = data[i];
            if(writepos>=size)
                writepos = 0;
            if(total < size)
                total ++;
            else{
                readpos = writepos + 1;
                if(readpos >= size)
                    readpos = 0;
            }
        }
    }

    public int get(){

        int dat=0xFF;
        if(total>0){
            total --;
            dat = byteBuffer[readpos ++];
            if(readpos>=size)
                readpos = 0;
        }
        return dat;
    }

    public boolean hasRemaining()
    {
        return total>0;
    }

    public void clear()
    {
        size=2048;
        readpos=0;
        writepos = 0;
        total = 0;
    }

    public static byte [] Create(int cmd, byte [] data){
        int i;
        byte  length;
        byte [] command = new byte [10];// 整条协议
        if (cmd < 0x7F) {
            length = gPackLength[cmd & 0xFF];// 当前指令的长度
        } else {
            return command;
        }

        command[0] = (byte)cmd;// 包类型ID
        command[1] = 0;


        // 生成数据头
        for (i = 0; i < data.length; i++) {
            command[1] |= ((data[i] & 0xFF) >> 7 << i);// 数据头依次包含数据字节的最高位，如：数据头的bit0为数据1的bit7，数据头的bit1为数据2的bit7
            Log.e(TAG, "Create: "+command[1] );
        }
        //放入原始数据
        for(i=0;i<length-3;i++){
            command[i+2]=data[i];
        }


        // 生成校验和，校验和为从头开始到最后一个数的累加和，都是原始数据
        for (i = 0; i < length - 1; i++) {
            command[length - 1] += command[i];
        }

        // 把除命令ID以外的所有数据的bit7置1
        for (i = 1; i < command.length; i++) {
            command[i] |= 0x80;
        }
        Log.e(TAG, "Create: command="+ StringUtils.byte2HexStr(command));
        return command;

    }

    /**
     * 解析接收到的数据 先根据第一个字节查表取得这条协议的长度，然后根据数据头，把数据转成真实的数据，最后检查校验和
     */
    synchronized public void Resolve( byte [] data, int offset, int count) {

        //测试用，完整数：08 80 88 A9 B9 真实数据：08 00 08 29 39
        //测试用，完整数：10 80 FF 8F 真实数据： 10 00 7F 0F 8F

        if (count > remaining()) {
            clear();
        }
        put(data, count);//放入数据

        int cmdID;//协议头
        int length;// 协议长度
        while (hasRemaining()) {
            cmdID = -1;
            length = -1;
            while (hasRemaining()) {
                cmdID = get() & 0xFF;// 为何要和
                if (cmdID < gPackLength.length) {
                    length = gPackLength[cmdID];
                }
                if (length > 0)
                    break;
            }
           // Log.e(TAG, "Resolve: 267="+Integer.toHexString(cmdID));

            // 没有找到指令，或者长度不够，返回
            if (length < 2 || length - 1 > remaining()) {
               // Log.e("Command", "剩余长度不?");
                return;
            }

            byte [] content = new byte [1024];
            if (length > 3) {


                // 根据数据头，把后面的数据转成真实的数据
                // 数据头依次包含数据字节的最高位，如：数据头的bit0为数据1的bit7，数据头的bit1为数据2的bit7
                byte  dataHead =  (byte)(get() & 0x7F);// 协议中的数据头

                for (int i = 0; i < length - 3; i++) {
                    // 从数据头中，获取出当前数据的bit7，然后或上0x7F
                    byte  bit7 = (byte)(dataHead << (7 - i) | 0x7F);// 结果只能是0x7F 或 0xFF，7F的表示下面数据的最高位应该是0，FF的表示下面数据的最高位应该是1

                    content[i] =(byte)(get() & bit7);//与上bit7后，得到真实的数据
                }

                // 检查校验和
                // 校验和，是把前面的所有字节 直接相加，假设指令长度为10，则是把前9个字节直接相加

                int checkSum = get();

                byte  target = 0;
                target += cmdID;// 包ID
                target += dataHead;// 数据头
                for (int i = 0; i < length - 3;i++) {
                    target += content[i];
                }

                target |= 0x80;//// 校验和的bit7是1，所以要或上0x80

                if (checkSum != target) {
                    //Log.e("Command", "err ...checkSum:"+checkSum+" target:"+target);
                    //int [] temp = new int [length];
                    //int Buffer.get(temp, 0, length);
//                    Log.e("Command", "无法解析的指令：");
                    continue;
                }else{
//                    Log.e("Command", "解析的指令：");
                    byte[] datas = DataUtils.subByteArr(content, 0, length-3);

                        resolveCommandListener.ResolveCommandHandle(cmdID,datas);
                }
            }
        }
    }

    private int getOffset(byte[] data, int cmdID) {
        int offset=0;
        for (int i = 0 ; i<data.length; i++){
            if (data[i]==cmdID){
                offset=i;
                break;
            }
        }
        return offset;
    }

    public interface ResolveCommandListener{
        void ResolveCommandHandle(int cmd, byte[] data);
    }
}
